﻿using System;
using System.Collections.Generic;
using System.Text;
using Dokan;
using Microsoft.Win32;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;

namespace HTML_FS
{
    # region httpfs class
    class htmlfs : DokanOperations
    {
        #region DokanOperations member
        //private string TopDirectory;
        private Dictionary<string, string> FS;
        private string URL_; //TOP level URL
        public string status;
        private struct FS_FILE
        {
            public Int64 inode;
            public string Name;
            public Uri Url;
            public Int64 Parent_inode;
        }

        public htmlfs(string URL)
        {
            URL_ = URL;
            FS = new Dictionary<string, string>();
            status = "";
        }

        public void changeURL(string URL)
        {
            URL_ = URL;
            FS.Clear();
               
        }
         
        public int Cleanup(string filename, DokanFileInfo info)
        {
            return 0;
        }

        public int CloseFile(string filename, DokanFileInfo info)
        {
            return 0;
        }

        public int CreateDirectory(string filename, DokanFileInfo info)
        {
            return 0;
        }

        public int CreateFile(
            string filename,
            System.IO.FileAccess access,
            System.IO.FileShare share,
            System.IO.FileMode mode,
            System.IO.FileOptions options,
            DokanFileInfo info)
        {
            return 0;
        }

        public int DeleteDirectory(string filename, DokanFileInfo info)
        {
            return 0;
        }

        public int DeleteFile(string filename, DokanFileInfo info)
        {
            return 0;
        }


        private string FetchUrl(string URL)
        {
            StringBuilder sb = new StringBuilder();

            // used on each read operation
            byte[] buf = new byte[8192];

            // prepare the web page we will be asking for
            HttpWebRequest request = (HttpWebRequest)
                WebRequest.Create(URL);

            // execute the request
            HttpWebResponse response = (HttpWebResponse)
                request.GetResponse();

            // we will read data via the response stream
            Stream resStream = response.GetResponseStream();

            string tempString = null;
            int count = 0;

            do
            {
                // fill the buffer with data
                count = resStream.Read(buf, 0, buf.Length);

                // make sure we read some data
                if (count != 0)
                {
                    // translate from bytes to ASCII text
                    tempString = Encoding.ASCII.GetString(buf, 0, count);

                    // continue building the string
                    sb.Append(tempString);
                }
            }
            while (count > 0); // any more data to read?
            return sb.ToString();
        } //HTML page

        public int FlushFileBuffers(
            string filename,
            DokanFileInfo info)
        {
            return 0;
        }

        private string UrlToFileName(string URL)
        {
            /*
                        Порядок замены символов не принимаемых Windows как имя файла/каталога. При этом этих символов не должно быть в URI
            ?	-		В опрос
            / 	-		Ч ерта
            :	-		ж
            */

            string S = URL.Replace("?", "В");
            S = S.Replace("/", "Ч");
            S = S.Replace(":", "ж");
            return S;
        }

        private string FileNameToUrl(string FileName)
        {
            /*
                        Порядок замены символов не принимаемых Windows как имя файла/каталога
            ?	-		В
            / 	-		Ч
            :	-		ж
            */

            string S = FileName.Replace("В", "?");
            S = S.Replace("Ч", "/");
            S = S.Replace("ж", ":");
            return S;
        }


        public int FindFiles(
            string filename,
            System.Collections.ArrayList files,
            DokanFileInfo info)
        {
            string wfilename = "";
            if (filename == "\\")
            {
                FileInformation finfo = new FileInformation();
                //FS.Clear();
                finfo.FileName = UrlToFileName(URL_);
                if (!FS.ContainsKey(finfo.FileName))
                {
                    FS.Add(finfo.FileName, URL_);
                }

                //TopDirectory = finfo.FileName;

                finfo.Attributes = System.IO.FileAttributes.Directory;
                finfo.LastAccessTime = DateTime.Now;
                finfo.LastWriteTime = DateTime.Now;
                finfo.CreationTime = DateTime.Now;
                files.Add(finfo);
                return 0;
            }

            else
            {
                write_log("**************************************************************");
                wfilename = Path.GetFileName(filename);
                write_log(wfilename + " *** " + Path.GetDirectoryName(filename));


                if (FS.ContainsKey(wfilename)) // True
                {
                    write_log(wfilename + " - " + FS[wfilename]);
                    wfilename = FS[wfilename];
                }
                else return -1;


                string page = FetchUrl(wfilename);

                if (page.Length <= 0)
                    return -1;

                //FS.Clear();

                //Regex url = new Regex(@"http\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?$", RegexOptions.Multiline);
                Regex url = new Regex(@"http\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(([a-zA-Z0-9\.\,\?\\\+&amp;%\$\=~_\-/])*)?", RegexOptions.Multiline); //Ищем URL в HTML тексте
                MatchCollection mx = url.Matches(page);
                foreach (Match m in mx)
                {
                    if (!Uri.IsWellFormedUriString(m.Value,UriKind.RelativeOrAbsolute)) continue;

                    FileInformation finfo = new FileInformation();
                    finfo.FileName = UrlToFileName(m.Value); //Преобразовываем URI в поянтные для системы символы (смотри замены)
                    string[] A = { Path.GetFileName(finfo.FileName) };
                    string[] tmpS = filename.Split(A, StringSplitOptions.None);
                    int count = tmpS.Length; //долой рекурсии
                    if (count > 1)
                    {
                        write_log(count.ToString());
                        continue;  //Долой рекурсии - пропускаем ссылку, если она присутвует в пути (google.ru/google.ru/google.ru....)
                    }
                    finfo.FileName = Path.GetFileName(finfo.FileName);
                    write_log(finfo.FileName);
                    if (!FS.ContainsKey(finfo.FileName))
                    {
                        FS.Add(finfo.FileName, m.Value);
                    }

                    finfo.Attributes = System.IO.FileAttributes.Directory;
                    if (new Regex(@"\.(jpg|gif|swf|png|svg|mp3|mp4|avi|wma|wmv|css|js|ico|rar|zip|7z|tar|gz|gzip)$").IsMatch(finfo.FileName.ToLower())) //Это мы определяем как файлы
                    {
                        finfo.Attributes = System.IO.FileAttributes.Normal;
                        finfo.Length = 100;
                    }
                    finfo.LastAccessTime = DateTime.Now;
                    finfo.LastWriteTime = DateTime.Now;
                    finfo.CreationTime = DateTime.Now;
                    count = 0;
                    foreach (FileInformation c in files) //Удаляем дублирующиеся ссылки (откуда только берутся?!)
                        if (c.FileName == finfo.FileName)
                            count++;
                    if (count <= 0)
                        files.Add(finfo);
                }
                return 0;
            }
        }


        public int GetFileInformation(
            string filename,
            FileInformation fileinfo,
            DokanFileInfo info)
        {
            if (filename == "\\")
            {
                fileinfo.Attributes = System.IO.FileAttributes.Directory;
                fileinfo.LastAccessTime = DateTime.Now;
                fileinfo.LastWriteTime = DateTime.Now;
                fileinfo.CreationTime = DateTime.Now;
                return 0;
            }

            if (FS.ContainsKey(filename))
            {
                fileinfo.Attributes = System.IO.FileAttributes.Directory;
                fileinfo.LastAccessTime = DateTime.Now;
                fileinfo.LastWriteTime = DateTime.Now;
                fileinfo.CreationTime = DateTime.Now;

                return 0;
            }
            else return -1;
        }

        public int LockFile(
            string filename,
            long offset,
            long length,
            DokanFileInfo info)
        {
            return 0;
        }

        public int MoveFile(
            string filename,
            string newname,
            bool replace,
            DokanFileInfo info)
        {
            return -1;
        }

        public int OpenDirectory(string filename, DokanFileInfo info)
        {
            //write_log("####### " + filename);
            return 0;
        }

        public int ReadFile(
            string filename,
            byte[] buffer,
            ref uint readBytes,
            long offset,
            DokanFileInfo info)
        {
            return 0;
        }

        public int SetEndOfFile(string filename, long length, DokanFileInfo info)
        {
            return -1;
        }

        public int SetAllocationSize(string filename, long length, DokanFileInfo info)
        {
            return -1;
        }

        public int SetFileAttributes(
            string filename,
            System.IO.FileAttributes attr,
            DokanFileInfo info)
        {
            return 0;
        }

        public int SetFileTime(
            string filename,
            DateTime ctime,
            DateTime atime,
            DateTime mtime,
            DokanFileInfo info)
        {
            return -1;
        }

        public int UnlockFile(string filename, long offset, long length, DokanFileInfo info)
        {
            return 0;
        }

        public int Unmount(DokanFileInfo info)
        {
            return 0;
        }

        public int GetDiskFreeSpace(
           ref ulong freeBytesAvailable,
           ref ulong totalBytes,
           ref ulong totalFreeBytes,
           DokanFileInfo info)
        {
            freeBytesAvailable = 0;
            totalBytes = 0;
            totalFreeBytes = 0;
            return 0;
        }

        public int WriteFile(
            string filename,
            byte[] buffer,
            ref uint writtenBytes,
            long offset,
            DokanFileInfo info)
        {
            return -1;
        }

        #endregion

        public void write_log(string Value)
        {
            status+='\n'+DateTime.Now.ToString()+" - "+Value;
        }
    }
# endregion httpfs class
}
